﻿using UnityEngine;
using System.Collections;

[RequireComponent(typeof(NetworkView))]
public class BehaviorUnits : MonoBehaviour 
{
	public bool _isSelected = false;
    public bool _isDirectSelected = false;
    public static bool _isTotemSelected = false;
    public int _nbHP = 10;
    public float _distanceAttack = 2f;
    public int _damage = 1;
    public float _attackSpeed = 1f;
    public float _speed = 10f;
    public float _timeCheckClouds = 0.2f;

    protected bool _canAttack = true;
    protected Vector3 _lastDestination = Vector3.zero;
    protected int _hpMax;

    protected NavMeshAgent _navMeshAgent;
    protected static GameObject _Totem = null;
	protected static GameObject _objectDestination = null;
    protected GameObject _target = null;
	protected GameObject _cloudAssociate = null;
    protected CloudBehavior _scriptCloud = null;
    protected StateMachine fsm = new StateMachine();
    

    protected class StateMachine : Fsm
    {
        public BehaviorUnits m_behavior;

        public override void OnStateUpdate(FsmStateEvent eEvent, uint a_oState)
        {
            switch ((State)a_oState)
            {
                case State.Idle:
                    m_behavior.StateIdle(eEvent);
                    break;
                case State.Moving:
                    m_behavior.StateMove(eEvent);
                    break;
                case State.Attack:
                    m_behavior.StateAttack(eEvent);
                    break;
                case State.Havesting:
                    m_behavior.StateHarvesting(eEvent);
                    break;
                case State.Deposit:
                    m_behavior.StateDeposit(eEvent);
                    break;
            }
        }
    }

    public enum State
    {
        Idle = 0,       //Just an animation, maybe aggro zone
        Moving,     //Move to a specific point
        Havesting,  // Only for havester. Move to a ressource point then harvest
        Deposit,  //Only for havester. Move to Totem then deposit ressource
        Attack //Obviously only for armed units
    }

    protected State _actualState = State.Idle;


    void Awake()
    {
        fsm.m_behavior = this;
    }

	// Use this for initialization
	void Start () 
	{
        _hpMax = _nbHP;

        SetState(State.Idle);

        if (networkView.isMine)
        {
            _navMeshAgent = this.GetComponent<NavMeshAgent>();
            _navMeshAgent.speed = _speed;
            if (_Totem == null)
            {
                GameObject[] totems = GameObject.FindGameObjectsWithTag(ListTags.tagTotem);
                if (totems[0].networkView.isMine)   // Only 2 totem. Should work even if the second one hasn't spawn.
                {
                    _Totem = totems[0];
                }
                else
                {
                    _Totem = totems[1];
                }
            }

			if (_objectDestination == null)
			{
				_objectDestination = GameObject.CreatePrimitive(PrimitiveType.Sphere);
				_objectDestination.renderer.material.color = Color.blue;
			}

            StartCoroutine(CheckCloud());
        }

       
	}
	
	// Update is called once per frame
	void Update () 
	{
        if (networkView.isMine)
        {
            HandleSelection();
            DirectClick();
            fsm.UpdateFsm(Time.deltaTime); // DO NOT FORGET THIS SHIT Maybe update every 0.1s
        }
        HandleDeath();
	}

    public virtual void StateIdle(FsmStateEvent eEvent)
    {
        switch (eEvent)
        {
            case FsmStateEvent.eEnter:
                {
                    //PlayAnim
                }
                break;
            case FsmStateEvent.eLeave:
                {
                     
                }
                break;
        }
    }

    public virtual void StateMove(FsmStateEvent eEvent)
    {
        switch (eEvent)
        {
            case FsmStateEvent.eEnter:
                {
                    //Play anim
                    Vector3 transition = new Vector3(); //Point to look at
                    transition = _lastDestination;
                    transition.y = this.transform.position.y;

                    LookAt(transition);
                    _navMeshAgent.SetDestination(_lastDestination);
					_objectDestination.transform.position = _lastDestination;
                }
                break;
            case FsmStateEvent.eUpdate:
                {
                    if (_navMeshAgent.remainingDistance < 1f)
                    {
                        SetState(State.Idle);
                    }
                    else if (_navMeshAgent.destination.x != _lastDestination.x || _navMeshAgent.destination.z != _lastDestination.z)// Do not check for y position
                    {
                        _navMeshAgent.SetDestination(_lastDestination);
						_objectDestination.transform.position = _lastDestination;
                    }
                }
                break;

        }
    }

    public virtual void StateAttack(FsmStateEvent eEvent)
    {
        switch (eEvent)
        {
            case FsmStateEvent.eEnter:
                {
                    if (_target == null)
                    {
                        SetState(State.Idle);
                    }
                    else
                    {
                        LookAt(_target.transform.position);
                        _navMeshAgent.SetDestination(_target.transform.position);
                    }
                }
                break;
            case FsmStateEvent.eUpdate:
                {
                    if (_target == null) //If target is dead. Add check when death != gameobject destroyed
                    {
                        SetState(State.Idle);
                    }
                    else if (Vector3.Distance(this.transform.position,_target.transform.position) > _distanceAttack) //If too far away from target, move to it
                    {
                        _navMeshAgent.SetDestination(_target.transform.position);
                    }
                    else if (_canAttack) //If not, launch attack exept if cooldown have not been reinitialised.
                    {
                        StartCoroutine(DealDamage());
                    }
                }
                break;
        }
        
    }

    public virtual void StateHarvesting(FsmStateEvent eEvent)
    {
        Debug.LogError("ERROR: THIS UNIT IS NOT AN HARVESTER");
    }

    public virtual void StateDeposit(FsmStateEvent eEvent)
    {
        Debug.LogError("ERROR: THIS UNIT IS NOT AN HARVESTER");
    }

    public virtual void SetEnemy(GameObject enemy) // Replace target
    {
        _target = enemy;
    }

    public virtual void LookAt(Vector3 objectToLook, bool vector = false) // If vector = true, objectToLook is a vector and not a position
    {
        if (!vector)
        {
            Vector3 transition = new Vector3();
            transition = objectToLook;
            transition.y = this.transform.position.y;
            this.transform.LookAt(transition);
        }
    }

    public virtual void SetDestination(Vector3 destination, string tagClick)
    {
        if (tagClick == ListTags.tagUnits)
        {
            SetState(State.Attack);
        }
        else
        {
            _lastDestination = destination;
            SetState(State.Moving);

        }
    }

    public virtual void GainLife(int nbGain) //Call outside of this script to increase health of nbGain
    {
        if (_nbHP + nbGain < _hpMax)
        {
            _nbHP += nbGain;
        }
        else
        {
            _nbHP = _hpMax;
        }
    }


    //Protected functions
    protected void SetState(State newState)
    {
        fsm.SetRequestedState((uint)newState);
    }

    protected virtual void HandleDeath()
    {
        if (_nbHP <= 0)
        {
            Destroy(this.gameObject);
            if (networkView.isMine)
            {
                _scriptCloud._listUnit.Remove(this);
            }
        }
    }

   
	 protected virtual void HandleSelection()
	{
		if (SelectionQuad.selectionRect.width < 0)
		{
			SelectionQuad.selectionRect.x += SelectionQuad.selectionRect.width;
			SelectionQuad.selectionRect.width = - SelectionQuad.selectionRect.width;
		}
		if (SelectionQuad.selectionRect.height < 0)
		{
			SelectionQuad.selectionRect.y += SelectionQuad.selectionRect.height;
			SelectionQuad.selectionRect.height = -SelectionQuad.selectionRect.height;
		}

		if (renderer.isVisible && Input.GetMouseButton(0))
		{
			Vector3 posOnScreen = Camera.main.WorldToScreenPoint(transform.position);
			posOnScreen.y = SelectionQuad.InverseYForScreen(posOnScreen.y);
			if (SelectionQuad.selectionRect.Contains(posOnScreen))
            {
                _isSelected = true;
            }
            else if (_isSelected && new Rect(10, 100, 120, 20).Contains(new Vector2(Input.mousePosition.x, Screen.height - Input.mousePosition.y)) && _isTotemSelected) // Not best check. Change not dynamic 
            {
                _isSelected = true;
            }
            else //Did that for some readability
            {
                _isSelected = false;
            }
		}

		if (_isSelected || _isDirectSelected) 
		{
			renderer.material.color = Color.red;
			if (!SelectionQuad.UnitsSelected.Contains(this.gameObject))
				SelectionQuad.UnitsSelected.Add(this.gameObject);
		}
			
		else
		{
			renderer.material.color = Color.white;
			if (SelectionQuad.UnitsSelected.Contains(this.gameObject))
				SelectionQuad.UnitsSelected.Remove(this.gameObject);
		}
	}


     //private functions
    void DirectClick()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Ray rayon = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if (Physics.Raycast(rayon,out hit, 10000))
            {
                if (hit.collider == this.collider)
                {
                    _isDirectSelected = true;
                }
                else
                {
                   _isDirectSelected = false;
                }
            }
        }
    }

    //Syncrhonisation functions
    [RPC]
    public void SetAnimation (int nbAnim) //Link an animation with a number,  for optimisation purpose
    {
        //Test
        this.renderer.material.color = Color.green;
    }

    [RPC] 
    public void ComputeDamage(int dmg)
    {
        _nbHP -= dmg;
    }

    //Coroutine
    IEnumerator DealDamage()
    {
        _canAttack = false;
        _target.networkView.RPC("ComputeDamage", RPCMode.All, _damage);
        yield return new WaitForSeconds(_attackSpeed);
        _canAttack = true;
    }

    IEnumerator CheckCloud()
    {
        while (true)
        {
            RaycastHit hit;
            if (Physics.Raycast(this.transform.position, -Vector3.up, out hit))
            {
                if (hit.collider.tag == ListTags.tagClouds && (_cloudAssociate == null || hit.collider != _cloudAssociate.collider))
                {
                    if (_scriptCloud != null)
                    {
                        _scriptCloud._listUnit.Remove(this);
                    }
                    _cloudAssociate = hit.collider.gameObject;
                    _scriptCloud = _cloudAssociate.GetComponent<CloudBehavior>();
                    _scriptCloud._listUnit.Add(this);
                }
            }
            yield return new WaitForSeconds(_timeCheckClouds);
        }
    }
}
